﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Diagnostics;
using System.Reflection;

namespace IRC.IRepairClouds.DataLayer.Expressions
{
    [DebuggerDisplay(@"\{{Expression}}")]
    public abstract class ExpressionBuilder
    {
        public Expression Expression { get; private set; }

        public ExpressionBuilder(Expression expression)
        {
            Expression = expression;
        }

        public static implicit operator Expression(ExpressionBuilder expressionBuilder)
        {
            return expressionBuilder.Expression;
        }

        public static ConstantExpressionBuilder<T> Constant<T>(T value)
        {
            return Expression.Constant(value, typeof(T));//.ToExpressionBuilder<T>();
        }

        public static ParameterExpressionBuilder<T> Parameter<T>(string name)
        {
            return Expression.Parameter(typeof(T), name);//.ToExpressionBuilder<T>();
        }

        public static NewArrayExpressionBuilder<T[]> NewArrayInit<T>(params ExpressionBuilder<T>[] initializers)
        {
            return Expression.NewArrayInit(typeof(T), initializers.Select(i => i.Expression).ToArray());//.ToExpressionBuilder<T[]>();
        }

        public static NewArrayExpressionBuilder<Array> NewArrayBounds<T>(params ExpressionBuilder<Int32>[] bounds)
        {
            return Expression.NewArrayBounds(typeof(T), bounds.Select(i => i.Expression).ToArray());//.ToExpressionBuilder<Array>();
        }

        public static NewArrayExpressionBuilder<T[,]> NewArrayBounds<T>(ExpressionBuilder<Int32> bound1, ExpressionBuilder<Int32> bound2)
        {
            return Expression.NewArrayBounds(typeof(T), bound1, bound2);//.ToExpressionBuilder<T[,]>();
        }

        public static NewArrayExpressionBuilder<T[,,]> NewArrayBounds<T>(ExpressionBuilder<Int32> bound1, ExpressionBuilder<Int32> bound2, ExpressionBuilder<Int32> bound3)
        {
            return Expression.NewArrayBounds(typeof(T), bound1, bound2, bound3);//.ToExpressionBuilder<T[,,]>();
        }

        public static NewExpressionBuilder<T> New<T>()
        {
            return Expression.New(typeof(T));//.ToExpressionBuilder<T>();
        }

        public static MemberInitExpressionBuilder<T> MemberInit<T>(NewExpressionBuilder<T> newExpression, params MemberBindingBuilder<T>[] bindings)
        {
            return Expression.MemberInit(newExpression.SpecificExpression, bindings.Select(i => i.MemberBinding).ToArray());//.ToExpressionBuilder<T>();
        }

        public static MemberAssignmentBuilder<T, TRes> Bind<T, TRes>(Expression<Func<T, TRes>> selector, ExpressionBuilder<TRes> expression)
        {
            if (selector.Body.NodeType != ExpressionType.MemberAccess)
                throw new NotSupportedException("selector");
            MemberExpression mexp = selector.Body as MemberExpression;
            switch (mexp.Member.MemberType) {
                case MemberTypes.Field:
                    return new MemberAssignmentBuilder<T, TRes>(mexp.Member, expression);
                case MemberTypes.Property:
                    return new MemberAssignmentBuilder<T, TRes>(((PropertyInfo)mexp.Member).GetSetMethod(), expression);
            }
            throw new NotSupportedException("selector");
        }

        public static MemberMemberBindingBuilder<T, TRes> MemberBind<T, TRes>(Expression<Func<T, TRes>> selector, params MemberBindingBuilder<TRes>[] bindings)
        {
            if (selector.Body.NodeType != ExpressionType.MemberAccess)
                throw new NotSupportedException("selector");
            MemberExpression mexp = selector.Body as MemberExpression;
            switch (mexp.Member.MemberType) {
                case MemberTypes.Field:
                    return new MemberMemberBindingBuilder<T, TRes>(mexp.Member, bindings);
                case MemberTypes.Property:
                    return new MemberMemberBindingBuilder<T, TRes>(((PropertyInfo)mexp.Member).GetGetMethod(), bindings);
            }
            throw new NotSupportedException("selector");
        }

        public static ConditionalExpressionBuilder<T> Condition<T>(ExpressionBuilder<bool> test, ExpressionBuilder<T> ifTrue, ExpressionBuilder<T> ifFalse)
        {
            return Expression.Condition(test, ifTrue, ifFalse);//.ToExpressionBuilder<T>();
        }


        public static ElementInitBuilder<T> ElementInit<T>(MethodInfo addMethod, params ExpressionBuilder[] arguments)
        {
            return new ElementInitBuilder<T>(addMethod, arguments);
        }

        public static ListInitExpressionBuilder<T> ElementInit<T>(NewExpressionBuilder<T> newExpression, params ElementInitBuilder<T>[] initializers)
        {
            return Expression.ListInit(newExpression.SpecificExpression, initializers.Select(i => i.ElementInit).ToArray());//.ToExpressionBuilder<T>();
        }
    }

    public partial class ExpressionBuilder<T> : ExpressionBuilder
    {
        public ExpressionBuilder(Expression expression)
            : base(expression)
        {
        }

        public static implicit operator Expression(ExpressionBuilder<T> expressionBuilder)
        {
            return expressionBuilder.Expression;
        }

        public static implicit operator ExpressionBuilder<T>(Expression expression)
        {
            return new ExpressionBuilder<T>(expression);
        }
    }
}
